Completed
Push — master ( 99051b...87e561 )
by Vitaly
29s
created

stacktracey.js ➔ ... ➔ methods.include

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 5

1 Function

Rating   Name   Duplication   Size   Complexity  
stacktracey.js ➔ ... ➔ ??? 0 1 ?
1
"use strict";
2
3
/*  ------------------------------------------------------------------------ */
4
5
const O            = Object,
0 ignored issues
show
Comprehensibility Best Practice introduced by
You seem to be aliasing the built-in name Object as O. This makes your code very difficult to follow, consider using the built-in name directly.
Loading history...
6
      isBrowser    = (typeof window !== 'undefined') && (window.window === window) && window.navigator,
7
      lastOf       = x => x[x.length - 1],
8
      getSource    = require ('get-source'),
9
      partition    = require ('./impl/partition'),
10
      asTable      = require ('as-table')
11
12
/*  ------------------------------------------------------------------------ */
13
14
class StackTracey extends Array {
15
16
    constructor (input, offset) {
17
        
18
        const originalInput          = input
19
            , isParseableSyntaxError = input && (input instanceof SyntaxError && !isBrowser)
20
        
21
        super ()
22
23
    /*  Fixes for Safari    */
24
25
        this.constructor = StackTracey
26
        this.__proto__   = StackTracey.prototype
27
28
    /*  new StackTracey ()            */
29
30
        if (!input) {
31
             input = new Error ()
32
             offset = (offset === undefined) ? 1 : offset
33
        }
34
35
    /*  new StackTracey (Error)      */
36
37
        if (input instanceof Error) {
38
            input = input[StackTracey.stack] || input.stack || ''
39
        }
40
41
    /*  new StackTracey (string)     */
42
43
        if (typeof input === 'string') {
44
            input = StackTracey.rawParse (input).slice (offset).map (StackTracey.extractEntryMetadata)
45
        }
46
47
    /*  new StackTracey (array)      */
48
49
        if (Array.isArray (input)) {
50
51
            if (isParseableSyntaxError) {
52
                
53
                const rawLines = module.require ('util').inspect (originalInput).split ('\n')
54
                    , fileLine = rawLines[0].match (/^([^:]+):(.+)/)
55
56
                if (fileLine) {
57
                    input.unshift ({
58
                        file: fileLine[1],
59
                        line: fileLine[2],
60
                        column: (rawLines[2] || '').indexOf ('^') + 1,
61
                        sourceLine: rawLines[1],
62
                        callee: '(syntax error)',
63
                        syntaxError: true
64
                    })
65
                }
66
            }
67
68
            this.length = input.length
69
            input.forEach ((x, i) => this[i] = x)
70
        }
71
    }
72
73
    static extractEntryMetadata (e) {
74
        
75
        const fileRelative = StackTracey.relativePath (e.file || '')
76
77
        return O.assign (e, {
78
79
            calleeShort:  e.calleeShort || lastOf ((e.callee || '').split ('.')),
80
            fileRelative: fileRelative,
81
            fileShort:    StackTracey.shortenPath (fileRelative),
82
            fileName:     lastOf ((e.file || '').split ('/')),
83
            thirdParty:   StackTracey.isThirdParty (fileRelative) && !e.index
84
        })
85
    }
86
87
    static shortenPath (relativePath) {
88
        return relativePath.replace (/^node_modules\//, '')
89
                           .replace (/^webpack\/bootstrap\//, '')
90
    }
91
92
    static relativePath (fullPath) {
93
        return fullPath.replace (isBrowser ? window.location.href : (process.cwd () + '/'), '')
94
                       .replace (/^.*\:\/\/?\/?/, '')
95
    }
96
97
    static isThirdParty (relativePath) {
98
        return (relativePath[0] === '~')                          || // webpack-specific heuristic
99
               (relativePath[0] === '/')                          || // external source
100
               (relativePath.indexOf ('node_modules')      === 0) ||
101
               (relativePath.indexOf ('webpack/bootstrap') === 0)
102
    }
103
104
    static rawParse (str) {
105
106
        const lines = (str || '').split ('\n')
107
108
        const entries = lines.map (line => { line = line.trim ()
109
110
            var callee, fileLineColumn = [], native, planA, planB
0 ignored issues
show
Unused Code introduced by
The assignment to variable fileLineColumn seems to be never used. Consider removing it.
Loading history...
111
112
            if ((planA = line.match (/at (.+) \((.+)\)/)) ||
113
                (planA = line.match (/(.*)@(.*)/))) {
114
115
                callee         =  planA[1]
116
                native         = (planA[2] === 'native')
117
                fileLineColumn = (planA[2].match (/(.*):(.+):(.+)/) || []).slice (1) }
118
119
            else if ((planB = line.match (/^(at\s+)*(.+):([0-9]+):([0-9]+)/) )) {
120
                fileLineColumn = (planB).slice (2) }
121
122
            else {
123
                return undefined }
124
125
            return {
126
                beforeParse: line,
127
                callee:      callee || '',
128
                index:       isBrowser && (fileLineColumn[0] === window.location.href),
129
                native:      native || false,
130
                file:        fileLineColumn[0] || '',
131
                line:        parseInt (fileLineColumn[1] || '', 10) || undefined,
132
                column:      parseInt (fileLineColumn[2] || '', 10) || undefined } })
133
134
        return entries.filter (x => (x !== undefined))
135
    }
136
137
    withSource (i) {
138
        return this[i] && StackTracey.withSource (this[i])
139
    }
140
141
    static withSource (loc) {
142
143
        if (loc.sourceFile || (loc.file && loc.file.indexOf ('<') >= 0)) { // skip things like <anonymous> and stuff that was already fetched
144
            return loc
145
            
146
        } else {
147
            let resolved = getSource (loc.file || '').resolve (loc)
148
149
            if (resolved.sourceFile) {
150
                resolved.file = resolved.sourceFile.path
151
                resolved = StackTracey.extractEntryMetadata (resolved)
152
            }
153
154
            if (resolved.sourceLine && resolved.sourceLine.includes ('// @hide')) {
155
                resolved.sourceLine  = resolved.sourceLine.replace  ('// @hide', '')
156
                resolved.hide = true
157
            }
158
159
            return O.assign ({ sourceLine: '' }, loc, resolved)
160
        }
161
    }
162
163
    get withSources () {
164
        return new StackTracey (this.map (StackTracey.withSource))
165
    }
166
167
    get mergeRepeatedLines () {
168
        return new StackTracey (
169
            partition (this, e => e.file + e.line).map (
170
                group => {
171
                    return group.items.slice (1).reduce ((memo, entry) => {
172
                        memo.callee      = (memo.callee      || '<anonymous>') + ' → ' + (entry.callee      || '<anonymous>')
173
                        memo.calleeShort = (memo.calleeShort || '<anonymous>') + ' → ' + (entry.calleeShort || '<anonymous>')
174
                        return memo }, O.assign ({}, group.items[0])) }))
175
    }
176
177
    get clean () {
178
        return this.withSources.mergeRepeatedLines.filter ((e, i) => (i === 0) || !(e.thirdParty || e.hide))
179
    }
180
181
    at (i) {
182
        return O.assign ({
183
184
            beforeParse: '',
185
            callee:      '<???>',
186
            index:       false,
187
            native:      false,
188
            file:        '<???>',
189
            line:        0,
190
            column:      0
191
192
        }, this[i])
193
    }
194
195
    static locationsEqual (a, b) {
196
        return (a.file   === b.file) &&
197
               (a.line   === b.line) &&
198
               (a.column === b.column)
199
    }
200
201
    get pretty () {
202
203
        const trimEnd   = (s, n) => (s.length > n) ? (s.slice (0, n-1) + '…') : s        
204
        const trimStart = (s, n) => (s.length > n) ? ('…' + s.slice (-(n-1))) : s
205
206
        return asTable (this.withSources.map (
207
                            e => [
208
                                ('at ' + trimEnd (e.calleeShort, 30)),
209
                                trimStart ((e.fileShort && (e.fileShort + ':' + e.line)) || '', 40),
210
                                trimStart (((e.sourceLine || '').trim () || ''), 80)
211
                            ]))
212
    }
213
214
    static resetCache () {
215
216
        getSource.resetCache ()
217
    }
218
219
    get asArray () {
220
221
    }
222
}
223
224
/*  Chaining helper for .isThirdParty
225
    ------------------------------------------------------------------------ */
226
227
(() => {
228
229
    const methods = {
230
231
        include (pred) {
232
233
            const f = StackTracey.isThirdParty
234
            O.assign (StackTracey.isThirdParty = (path => f (path) ||  pred (path)), methods)
235
        },
236
237
        except (pred) {
238
239
            const f = StackTracey.isThirdParty
240
            O.assign (StackTracey.isThirdParty = (path => f (path) && !pred (path)), methods)
241
        },
242
    }
243
244
    O.assign (StackTracey.isThirdParty, methods)
245
246
}) ()
247
248
/*  Array methods
249
    ------------------------------------------------------------------------ */
250
251
;['map', 'filter', 'slice', 'concat', 'reverse'].forEach (name => {
252
253
    StackTracey.prototype[name] = function (/*...args */) { // no support for ...args in Node v4 :(
254
        
255
        const arr = Array.from (this)
256
        return new StackTracey (arr[name].apply (arr, arguments))
257
    }
258
})
259
260
/*  A private field that an Error instance can expose
261
    ------------------------------------------------------------------------ */
262
263
StackTracey.stack = /* istanbul ignore next */ (typeof Symbol !== 'undefined') ? Symbol.for ('StackTracey') : '__StackTracey'
0 ignored issues
show
Bug introduced by
The variable Symbol seems to be never declared. If this is a global, consider adding a /** global: Symbol */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
264
265
/*  ------------------------------------------------------------------------ */
266
267
module.exports = StackTracey
268
269
/*  ------------------------------------------------------------------------ */
270
271